﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Parseq.Core
{
    public class CharParser : BaseParser 
    {
        private bool toNegate = false;
        public CharParser()
        {
        }
        public CharParser(bool toNegate) {
            this.toNegate = toNegate; 
        }

        public override Result InnerParse(IReader reader, Location pos)
        {
            if (!reader.Location.Equals(pos))
            {
                reader.Location = pos;
            }
            int ch;
            if ((ch = (int)reader.Peek()) != -1)
            {
                if (this.Test(Convert.ToChar(ch)))
                {
                    char c = (char)reader.Read();
                    return new Result(c, reader.Location); // position is updated. 
                }
                else
                {
                    return Result.MakeFail(reader, pos);
                }
            }
            else
            {
                return Result.MakeFail(reader, pos);
            }
        }

        public bool Test(char c)
        {
            return this.InnerTest(c) ^ this.toNegate; 
        }

        public bool ToNegate
        {
            get { return this.toNegate; }
            set { this.toNegate = value; } 
        }

        public CharParser SetToNegate(bool toNegate) 
        {
            this.ToNegate = toNegate; 
            return this; 
        }

        public virtual bool InnerTest(char c)
        {
            return true; 
        }
        // equal
        // not equal
        // in
        // not in
        // range
        // composite (one-of) 
        // custom 
        // and I want the ability to negate them! 
        // so this can be done via composite or subclassing - I would prefer composite if possible, of course. 
        // testDelegate, name, toStringDelegate , plus potential "objects" to hold (such as the character to be equaled or the range, etc). 
        // in a way - it's either a subclass @ the delegate level or subclass @ the parser level - either way we'll require that.... 
        // let's do it @ the parser level, and we'll move the code back to the integer level. 

        public override string ToString()
        {
            return string.Format("{{{0}:{1}{2}}}", this.GetType().ToString(), this.verbToString(), this.paramToString()); 
        }

        protected virtual string verbToString()
        {
            if (this.toNegate)
            {
                return "not abstract"; 
            }
            else
            {
                return "abstract";
            }
        }

        protected virtual string paramToString()
        {
            return ""; 
        }

    }

    public class CharEquals : CharParser
    {
        private char c;
        public CharEquals(char c, bool toNegate)
            : base(toNegate)
        {
            this.c = c; 
        }

        public CharEquals(char c) : base(false) 
        {
            this.c = c; 
        }

        public override bool InnerTest(char c)
        {
            return c == this.c; 
        }

        protected override string verbToString()
        {
            if (this.ToNegate)
            {
                return "!=";
            }
            else
            {
                return "=="; 
            }
        }

        protected override string paramToString()
        {
            return this.c.ToString(); 
        }
    }

    public class CharIn : CharParser 
    {
		private ArrayList members; 
        public CharIn(char[] members, bool toNegate)
            : base(toNegate)
        {
            this.members = new ArrayList(members); 
			this.members.Sort(); 
        }

        public CharIn(char[] members)
            : base(false)
        {
            this.members = new ArrayList(members); 
			this.members.Sort(); 
        }
		
		public void Add(char c) {
			this.members.Add(c); 
			this.members.Sort(); 
		}
		
		
        public override bool InnerTest(char c)
        {
			return this.members.BinarySearch(c) >= 0; 
        }

        protected override string verbToString()
        {
            if (this.ToNegate)
            {
                return "not in";
            }
            else
            {
                return "in"; 
            }
        }
        protected override string paramToString()
        {
            return Util.String.ArrayToString(this.members.ToArray()); 
        }
    }

    public class CharBetween : CharParser
    {
        private char low, high;
        public CharBetween(char low, char high, bool toNegate)
            : base(toNegate)
        {
            this.low = low;
            this.high = high; 
        }

        public CharBetween(char low, char high)
            : base(false)
        {
            this.low = low;
            this.high = high; 
        }

        public override bool InnerTest(char c)
        {
            return this.low <= c && c <= this.high; 
        }

        public override string  ToString()
        {
            return string.Format("{{{0}:between {1} and {2}}}", this.GetType().ToString(), this.low, this.high); 
        }
    }

    public class CharOneOf : CharParser
    {
        private CharParser[] inners;

        public CharOneOf(CharParser[] inners, bool toNegate)
            : base(toNegate)
        {
            this.inners = inners; 
        }

        public CharOneOf(CharParser[] inners)
            : base(false)
        {
            this.inners = inners; 
        }

        public override bool InnerTest(char c)
        {
            foreach (CharParser inner in this.inners)
            {
                if (inner.Test(c))
                    return true; 
            }
            return false; 
        }

        public override string ToString()
        {
            return string.Format("{{{0}:{1}}}", this.GetType().ToString(), Util.String.ArrayToString(this.inners)); 
        }
    }

    public class CharFollowedBy : CharParser
    {
        private CharParser first;
        private CharParser next;
        public CharFollowedBy(CharParser first, CharParser next)
        {
            this.first = first;
            this.next = next;
        }

        public override Result InnerParse(IReader reader, Location pos)
        {
            Result first = this.first.InnerParse(reader, pos);
            if (Result.Succeeded(first))
            {
                // do the next one. 
                Result next = this.next.InnerParse(reader, reader.Location);
                if (Result.Succeeded(next))
                {
                    return first;
                }
                else
                {
                    return Result.MakeFail(reader, pos);
                }
            }
            else
            {
                return Result.MakeFail(reader, pos); 
            }
        }
    }
}
